package com.adingxiong.cft.service.impl;

import cn.hutool.http.HttpUtil;
import com.adingxiong.cft.common.SystemDetails;
import com.adingxiong.cft.config.ApplicationProperties;
import com.adingxiong.cft.constant.TempalteEnum;
import com.adingxiong.cft.controller.oauth.MayunOauth;
import com.adingxiong.cft.entity.LoginLog;
import com.adingxiong.cft.entity.TUser;
import com.adingxiong.cft.entity.dto.OnlineUser;
import com.adingxiong.cft.entity.dto.UpdatePasswordParam;
import com.adingxiong.cft.entity.dto.UserRegister;
import com.adingxiong.cft.errors.Assert;
import com.adingxiong.cft.mapper.LoginLogMapper;
import com.adingxiong.cft.mapper.TUserMapper;
import com.adingxiong.cft.service.ILoginLogService;
import com.adingxiong.cft.service.ITUserService;
import com.adingxiong.cft.util.IpUtils;
import com.adingxiong.cft.util.JwtTokenUtil;
import com.adingxiong.cft.util.WordUtil;
import com.alibaba.fastjson.JSONObject;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.*;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * <p>
 * 用户表 服务实现类
 * </p>
 *
 * @author xiongc
 * @since 2020-09-22
 */
@Service
@Slf4j
public class TUserServiceImpl extends ServiceImpl<TUserMapper, TUser> implements ITUserService {


    @Autowired
    private JwtTokenUtil jwtTokenUtil;

    @Autowired
    private WordUtil wordUtil;

    private static SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");

    @Autowired
    private ApplicationProperties properties;

    @Autowired
    private ILoginLogService logService;

    @Autowired
    private LoginLogMapper loginLogMapper;


    @Autowired
    private PasswordEncoder passwordEncoder;

    @Override
    public String login(String username,String password) {
        String token = null;
        try {
            UserDetails userDetails = loadByUserName(username);
            if(!passwordEncoder.matches(password,userDetails.getPassword())){
                Assert.fail("密码错误");
            }
            if(!userDetails.isEnabled()){
                Assert.fail("账户被禁用");
            }

            UsernamePasswordAuthenticationToken authenticationToken = new UsernamePasswordAuthenticationToken(userDetails,null,userDetails.getAuthorities());
            SecurityContextHolder.getContext().setAuthentication(authenticationToken);
            token = jwtTokenUtil.generateToken(userDetails);
            //插入登录日志
            updateLoginTime(username);
        } catch (AuthenticationException e){
            log.warn("登录异常:{}",e.getMessage());
        }
        return token;
    }

    @Override
    @Transactional
    public void updateLoginTime(String username){
        TUser user = getUserByUsername(username);
        user.setLoginTime(new Date());
        updateById(user);

        HttpServletRequest request = ((ServletRequestAttributes)
                RequestContextHolder.getRequestAttributes()).getRequest();
        String ip = IpUtils.getRealIp(request);
        String  agnent = request.getHeader("user-agent");
        Long currentSeconds = System.currentTimeMillis();
        Long defaultOutTime = currentSeconds + properties.getExpiration()*1000;
        //插入或者修改用户登录日志
        LoginLog log = logService.getByUserId(user.getId());
        if(log == null ){
            log = new LoginLog();
            log.setUserId(user.getId());
            log.setLoginTime(new Date());
            log.setIp(ip);
            log.setUserAgent(agnent);
            //设置默认退出登录时间为  token失效时间
            log.setLogoutTime(new Date(defaultOutTime));
            logService.saveOrUpdate(log);
        } else {
            log.setLoginTime(new Date());
            log.setCount(log.getCount() + 1);
            log.setLogoutTime(new Date(defaultOutTime));
            log.setIp(ip);
            logService.updateById(log);
        }


    }

    @Override
    public TUser getUserByUsername(String username){
        TUser user = null;
        QueryWrapper<TUser> wrapper = new  QueryWrapper();
        wrapper.lambda().eq(TUser::getUsername,username);
        List<TUser> list = list(wrapper);
        if(!CollectionUtils.isEmpty(list)){
            user = list.get(0);
            return user;
        }
        return null;
    }

    @Override
    public UserDetails loadByUserName(String username) {
        TUser user = getUserByUsername(username);
        if(user != null){
            return new SystemDetails(user);
        }
        throw new UsernameNotFoundException("用户名不存在");
    }

    @Override
    public String refreshToken(String oldToken) {
        return jwtTokenUtil.refreshHeadToken(oldToken);
    }

    @Override
    public TUser register(UserRegister register) {
        TUser user = new TUser();
        BeanUtils.copyProperties(register,user);

        TUser check = getUserByUsername(user.getUsername());
        if(check != null){
            Assert.fail("注册失败，用户已存在");
        }

        user.setCreateTime(new Date());
        String encodingpwd = passwordEncoder.encode(user.getPassword());
        user.setPassword(encodingpwd);
        baseMapper.insert(user);
        return user;
    }

    @Override
    public Page<TUser> list(String keywords, Integer pageSize, Integer pageNum) {
        Page<TUser> page = new Page<>(pageNum,pageSize);
        QueryWrapper<TUser> wrapper = new QueryWrapper<>();
        LambdaQueryWrapper<TUser> lambda = wrapper.lambda();
        if(StringUtils.isNotEmpty(keywords)){
            lambda.eq(TUser::getUsername,keywords);
            lambda.or().like(TUser::getNickName,keywords);
        }
        return page(page,wrapper);
    }

    @Override
    public boolean update(Integer id, TUser user) {
        user.setId(id);
        TUser old = getById(id);
        if(old.getPassword().equals(user.getPassword())){
            user.setPassword(null);
        }else {
            if(StringUtils.isEmpty(user.getPassword())){
                user.setPassword(null);
            }else {
                user.setPassword(passwordEncoder.encode(user.getPassword()));
            }
        }
        return updateById(user);

    }

    @Override
    public int updatePassword(UpdatePasswordParam param) {
        TUser user = getUserByUsername(param.getUsername());
        if(user == null){
            Assert.fail("该用户不存在");
        }
        if(!passwordEncoder.matches(param.getOldPassword(),user.getPassword())){
            Assert.fail("旧密码输入错误");
        }
        if(param.getNewPassword().equals(param.getOldPassword())){
            Assert.fail("新密码寓旧密码重复");
        }
        user.setPassword(passwordEncoder.encode(param.getNewPassword()));
        updateById(user);
        return 1;
    }

    @Override
    public void logOut(HttpServletRequest request) {
        String token = request.getHeader(properties.getTokenHeader());
        if(token == null) {
            Assert.fail("用户未登录");
        }
        String username = jwtTokenUtil.getUserNameFromToken(token.substring(properties.getTokenHead().length()));
        if(username  == null ) {
            Assert.fail("token过期");
        }
        TUser user = getUserByUsername(username);

        boolean update = logService.update(user.getId(),new Date());
        if (!update) {
            Assert.fail("更新退出日志失败");
        }
    }

    @Override
    public List<OnlineUser> getOnline() {
        return loginLogMapper.findOnline();
    }

    @Override
    public void exportAsWord(HttpServletResponse response,HttpServletRequest request) throws IOException {
        TempalteEnum tempalteEnum = TempalteEnum.getByType("userInfo");

        TUser user = getById(4);
        Map<String,Object> param = new HashMap<>();
        param.put("date",sdf.format(new Date()));
        param.put("username",user.getUsername());
        param.put("nickname",user.getNickName());
        param.put("email",user.getEmail());
        param.put("note",user.getNote());
        wordUtil.exportWordAsEasy(tempalteEnum.getFileName(),param,response,request);
    }


    @Override
    public String getToken(String code) {
        Map<String,Object> params = new HashMap<>();
        params.put("grant_type","authorization_code");
        params.put("code",code);
        params.put("client_id",MayunOauth.clientId);
        params.put("redirect_uri",MayunOauth.callback);
        params.put("client_secret",MayunOauth.secret);
        String post = HttpUtil.post(MayunOauth.postToken, params);

        return post;
    }

    @Override
    public Map<String, Object> getUserInfo(String accessToken) {
        Map<String,Object> params = new HashMap<>();
        params.put("access_token",accessToken);
        String userInfo = HttpUtil.get(MayunOauth.userInfo, params);
        Map<String, Object> res  = (Map<String, Object>) JSONObject.parse(userInfo);
        return res;
    }
}
